package feces.request;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;

import feces.Config;
import feces.utility.Utility;

/**
 * 修饰HttpServletRequest请求对象以修正PUT和DELETE请求参数
 *
 * @author Bromine0x23
 */
public class HTTPRequest implements HttpServletRequest {
	
	/**
	 * 请求参数
	 */
	private Map<String, String[]> parameters = new HashMap<String, String[]>();

	/**
	 * 被包装的请求对象
	 */
	private HttpServletRequest request;

	/**
	 * 包装请求
	 * 
	 * @param request
	 */
	protected HTTPRequest(
		HttpServletRequest request
	) {
		this.request = request;
		fixParameter();
	}
	
	/**
	 * 修饰请求对象
	 * @param request
	 * @return 当request实现了{@link javax.servlet.http.HttpServletRequest}接口时
	 *         返回修饰后的对象，否则返回request
	 */
	public static ServletRequest facade(
		ServletRequest request
	) {
		return (request instanceof HttpServletRequest)
			? new HTTPRequest((HttpServletRequest) request)
			: request;
	}
	
	/**
	 * 简单包装
	 */
	@Override
	public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
		return request.authenticate(response);
	}

	/**
	 * 简单包装
	 */
	@Override
	public String changeSessionId() {
		return request.changeSessionId();
	}

	/**
	 * 简单包装
	 */
	@Override
	public AsyncContext getAsyncContext() {
		return request.getAsyncContext();
	}

	/**
	 * 简单包装
	 */
	@Override
	public Object getAttribute(String name) {
		return request.getAttribute(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public Enumeration<String> getAttributeNames() {
		return request.getAttributeNames();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getAuthType() {
		return request.getAuthType();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getCharacterEncoding() {
		return request.getCharacterEncoding();
	}

	/**
	 * 简单包装
	 */
	@Override
	public int getContentLength() {
		return request.getContentLength();
	}

	/**
	 * 简单包装
	 */
	@Override
	public long getContentLengthLong() {
		return request.getContentLengthLong();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getContentType() {
		return request.getContentType();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getContextPath() {
		return request.getContextPath();
	}

	/**
	 * 简单包装
	 */
	@Override
	public Cookie[] getCookies() {
		return request.getCookies();
	}

	/**
	 * 简单包装
	 */
	@Override
	public long getDateHeader(String name) {
		return request.getDateHeader(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public DispatcherType getDispatcherType() {
		return request.getDispatcherType();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getHeader(String name) {
		return request.getHeader(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public Enumeration<String> getHeaderNames() {
		return request.getHeaderNames();
	}

	/**
	 * 简单包装
	 */
	@Override
	public Enumeration<String> getHeaders(String name) {
		return request.getHeaders(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public ServletInputStream getInputStream() throws IOException {
		return request.getInputStream();
	}

	/**
	 * 简单包装
	 */
	@Override
	public int getIntHeader(String name) {
		return request.getIntHeader(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getLocalAddr() {
		return request.getLocalAddr();
	}

	/**
	 * 简单包装
	 */
	@Override
	public Locale getLocale() {
		return request.getLocale();
	}

	/**
	 * 简单包装
	 */
	@Override
	public Enumeration<Locale> getLocales() {
		return request.getLocales();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getLocalName() {
		return request.getLocalName();
	}

	/**
	 * 简单包装
	 */
	@Override
	public int getLocalPort() {
		return request.getLocalPort();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getMethod() {
		return request.getMethod();
	}

	/**
	 * 覆写的被包装对象方法
	 */
	@Override
	public String getParameter(String name) {
		String[] values = parameters.get(name);
		return values != null ? values[0] : null;
	}

	/**
	 * 覆写的被包装对象方法
	 */
	@Override
	public Map<String, String[]> getParameterMap() {
		return parameters;
	}

	/**
	 * 覆写的被包装对象方法
	 */
	@Override
	public Enumeration<String> getParameterNames() {
		return new Vector<String>(parameters.keySet()).elements();
	}

	/**
	 * 覆写的被包装对象方法
	 */
	@Override
	public String[] getParameterValues(String name) {
		return parameters.get(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public Part getPart(String name) throws IOException, ServletException {
		return request.getPart(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public Collection<Part> getParts() throws IOException, ServletException {
		return request.getParts();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getPathInfo() {
		return request.getPathInfo();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getPathTranslated() {
		return request.getPathTranslated();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getProtocol() {
		return request.getProtocol();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getQueryString() {
		return request.getQueryString();
	}

	/**
	 * 简单包装
	 */
	@Override
	public BufferedReader getReader() throws IOException {
		return request.getReader();
	}

	/**
	 * 简单包装
	 *
	 * @deprecated
	 */
	@Deprecated
	@Override
	public String getRealPath(String path) {
		return request.getRealPath(path);
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getRemoteAddr() {
		return request.getRemoteAddr();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getRemoteHost() {
		return request.getRemoteHost();
	}

	/**
	 * 简单包装
	 */
	@Override
	public int getRemotePort() {
		return request.getRemotePort();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getRemoteUser() {
		return request.getRemoteUser();
	}

	/**
	 * 简单包装
	 */
	@Override
	public RequestDispatcher getRequestDispatcher(String path) {
		return request.getRequestDispatcher(path);
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getRequestedSessionId() {
		return request.getRequestedSessionId();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getRequestURI() {
		return request.getRequestURI();
	}

	/**
	 * 简单包装
	 */
	@Override
	public StringBuffer getRequestURL() {
		return request.getRequestURL();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getScheme() {
		return request.getScheme();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getServerName() {
		return request.getServerName();
	}

	/**
	 * 简单包装
	 */
	@Override
	public int getServerPort() {
		return request.getServerPort();
	}

	/**
	 * 简单包装
	 */
	@Override
	public ServletContext getServletContext() {
		return request.getServletContext();
	}

	/**
	 * 简单包装
	 */
	@Override
	public String getServletPath() {
		return request.getServletPath();
	}

	/**
	 * 简单包装
	 */
	@Override
	public HttpSession getSession() {
		return request.getSession();
	}

	/**
	 * 简单包装
	 */
	@Override
	public HttpSession getSession(boolean create) {
		return request.getSession(create);
	}

	/**
	 * 简单包装
	 */
	@Override
	public Principal getUserPrincipal() {
		return request.getUserPrincipal();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isAsyncStarted() {
		return request.isAsyncStarted();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isAsyncSupported() {
		return request.isAsyncSupported();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isRequestedSessionIdFromCookie() {
		return request.isRequestedSessionIdFromCookie();
	}

	/**
	 * 简单包装
	 *
	 * @deprecated
	 */
	@Deprecated
	@Override
	public boolean isRequestedSessionIdFromUrl() {
		return request.isRequestedSessionIdFromUrl();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isRequestedSessionIdFromURL() {
		return request.isRequestedSessionIdFromURL();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isRequestedSessionIdValid() {
		return request.isRequestedSessionIdValid();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isSecure() {
		return request.isSecure();
	}

	/**
	 * 简单包装
	 */
	@Override
	public boolean isUserInRole(String role) {
		return request.isUserInRole(role);
	}

	/**
	 * 简单包装
	 */
	@Override
	public void login(String username, String password) throws ServletException {
		request.login(username, password);
	}

	/**
	 * 简单包装
	 */
	@Override
	public void logout() throws ServletException {
		request.logout();
	}

	/**
	 * 简单包装
	 */
	@Override
	public void removeAttribute(String name) {
		request.removeAttribute(name);
	}

	/**
	 * 简单包装
	 */
	@Override
	public void setAttribute(String name, Object object) {
		request.setAttribute(name, object);
	}

	/**
	 * 简单包装
	 */
	@Override
	public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
		request.setCharacterEncoding(env);
	}

	/**
	 * 简单包装
	 */
	@Override
	public AsyncContext startAsync() throws IllegalStateException {
		return request.startAsync();
	}

	/**
	 * 简单包装
	 */
	@Override
	public AsyncContext startAsync(
		ServletRequest servletRequest, ServletResponse servletResponse
	) throws IllegalStateException {
		return request.startAsync(servletRequest, servletResponse);
	}

	/**
	 * 简单包装
	 */
	@Override
	public <T extends HttpUpgradeHandler> T upgrade(
		Class<T> handlerClass
	) throws IOException, ServletException {
		return request.upgrade(handlerClass);
	}

	/**
	 * 转换参数
	 */
	private void convertParameter() {
		for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
			parameters.put(entry.getKey(), entry.getValue().clone());
		}
	}

	/**
	 * 修正请求参数
	 */
	private void fixParameter() {
		switch (getMethod()) {
		case "GET":
		case "POST":
			convertParameter();
			break;
		case "PUT":
		case "DELETE":
			parseParameter();
			break;
		default:
			break;
		}
	}
	
	/**
	 * 解析参数
	 */
	private void parseParameter() {
		if (Utility.isStartWith(getContentType(), "application/x-www-form-urlencoded")) {
			parseURLEncodedParameter();
		}
	}

	/**
	 * 解析URL编码格式的参数
	 */
	private void parseURLEncodedParameter() {
		try {
			byte[] body = readBody();
			String content = new String(body);
			for (String pair : content.split("&")) {
				String[] strings = pair.split("=", 2);
				String key = URLDecoder.decode(strings[0], Config.ENCODING);
				String value = URLDecoder.decode(strings[1], Config.ENCODING);
				String[] values = parameters.get(key);
				parameters.put(
					key,
					values != null
						? Utility.concat(values, value)
						: new String[] { value }
				);
			}
		} catch (IOException exception) {
			exception.printStackTrace();
		}
	}

	/**
	 * 读取请求体
	 */
	private byte[] readBody() throws IOException {
		InputStream input = getInputStream();
		try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
			byte[] buffer = new byte[0x1000];
			int read = 0;
			while ((read = input.read(buffer)) != -1) {
				output.write(buffer, 0, read);
			}
			return output.toByteArray();
		}
	}

}
